package com.yuwen.yuwenRPC.spi;
import cn.hutool.core.io.resource.ResourceUtil;
import com.yuwen.yuwenRPC.serializer.Serializer;
import lombok.extern.slf4j.Slf4j;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @ClassNameSpiLoader
 * @Description TODO
 * @Author钰玟
 * @Date2025/3/30 下午7:31
 * @Version 1.0
 **/
@Slf4j
public class SpiLoader {
    /**
     * 存储已经加载的类 (接口名 (键名  => 实现类))
     */
    private static final Map<String,Map<String,Class<?>>> loaderMap = new ConcurrentHashMap<>();

    /**
     * 对象实例缓存 避免重复new ， 实现类类名 => 实现类实例
     * ConcurrentHashMap(instanceCache): key:implClassName => value :ImplClassInstance
     */
    private static final Map<String,Object> instanceCache = new ConcurrentHashMap<>();

    /**
     * 系统SPI目录
     */
    private static final String SYSTEM_DIR = "META-INF/rpc/system/";

    /**
     * 用户自定义SPI目录
     */
    private static final String CUSTOM_DIR = "META-INF/rpc/custom/";

    /**
     * 扫描路径
     */
    private static final String[] SCAN_DIRS = new String[]{SYSTEM_DIR, CUSTOM_DIR};

    /**
     * 动态加载的扫描的 Serializer 列表
     */
    private static final List<Class<?>> SCAN_CLASSES_LIST = Arrays.asList(Serializer.class);

    /**
     * 加载所有的Serializer类
     */
    public static void loadAll(){
        log.info("加载所有的Serializer");
        for (Class<?> service : SCAN_CLASSES_LIST) {
            load(service);
        }
    }


    /**
     * 获取某个接口的实例
     * @param clazz 实例的类对象
     * @param key 键
     * @return T
     * @param <T> 泛型
     */
    //        存储已经加载的类 (接口名 (键名  => 实现类))  loaderMap
    public static <T> T getInstance(Class<T> clazz,String key){
        //1. 获取接口名
        String serviceName = clazz.getName();
        //2. 获取 loadMap  中的  键名  => 实现类
        Map<String,Class<?>> serviceMap = loaderMap.get(serviceName);
        //3. 判断是否为空
        if(serviceMap == null){
            //3.1 为空
            throw new RuntimeException(String.format("SpiLoader未加载 %s 类型", serviceName));
        }
        if(!serviceMap.containsKey(key)){
            //3.2 不存在 说明 未new 实例
            throw new RuntimeException(String.format("SpiLoader的%s 不存在 key = %s的实例", serviceName, key));
        }
        //3.3 不为空，且存在
        //4. 获取对应 key的 实例实现类型  ==> 多态
        Class<?> ImplClass = serviceMap.get(key);
        //5.从实例缓存加载指定的类型
        //5.1 获取实现的实例对象的名称
        String implClassName = ImplClass.getName();
        if(!instanceCache.containsKey(implClassName)){
            try{
                instanceCache.put(implClassName, ImplClass.newInstance());
            }
            catch (InstantiationException | IllegalAccessException e){
                String errorMsg = String.format("%s类实例化失败", ImplClass);
                throw new RuntimeException(errorMsg, e);
            }
        }
        return  (T) instanceCache.get(implClassName);
    }

    public static Map<String,Class<?>> load(Class<?> loadClass){
        log.info("加载类型为 {}的 SPI", loadClass.getName());

        //扫描路径 ，用户定义的SPI优先级大于 系统的SPI               //json=com.yuwen.yuwenRPC.serializer.JsonSerializer
        HashMap<String, Class<?>> keyClassMap = new HashMap<>(); // key:json => value:com.yuwen.yuwenRPC.serializer.JsonSerializer
        for (String scanDir : SCAN_DIRS) {
            //拼接扫描路径
            List<URL> resources = ResourceUtil.getResources(scanDir+loadClass.getName());
            for (URL resource : resources) {
                try {
                    InputStreamReader inputStreamReader = new InputStreamReader(resource.openStream());
                    //缓存流
                    BufferedReader bufferedReader = new BufferedReader(inputStreamReader);

                    String line ;
                    while((line = bufferedReader.readLine()) !=null){
                        String[] strArray = line.split("="); //json=com.yuwen.yuwenRPC.serializer.JsonSerializer
                        if(strArray.length>1){
                            String key = strArray[0];// key: json
                            String className = strArray[1];//ImplClass.Class
                            keyClassMap.put(key,Class.forName(className));//反射 => 根据类名得到对应得Class
                        }
                    }
                } catch (Exception e) {
                    log.error("spi resource load error ",e);
                }
            }
        }
        //放入 loaderMap中
        loaderMap.put(loadClass.getName(), keyClassMap);
        return keyClassMap;
    }

}
